package com.zzl.study.auth.security.authentication.context;

import com.zzl.study.auth.utils.JacksonUtils;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import org.springframework.util.CollectionUtils;

import java.net.URL;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author: zhangzl
 * @date: 2024/6/6 14:18
 * @version: 1.0
 * @description: 自定义OIDC用户
 */
@Data
public class OidcUser {

    private List<OidcAuthority> authorities;

    private Claims attributes;

    private IdToken idToken;

    private UserInfo userInfo;

    private Claims claims;

    private String name;

    private URL issuer;

    private Instant expiresAt;

    private Instant issuedAt;

    private String authorizationCodeHash;

    private List<String> authenticationMethods;

    private String authenticationContextClass;

    private String nonce;

    private List<String> audience;

    private String accessTokenHash;

    private String authorizedParty;

    private Instant authenticatedAt;

    private String subject;

    private Address address;

    private String locale;

    private String fullName;

    private String zoneInfo;

    private String profile;

    private String preferredUsername;

    private Boolean phoneNumberVerified;

    private String familyName;

    private String nickName;

    private Instant updatedAt;

    private String picture;

    private Boolean emailVerified;

    private String website;

    private String email;

    private String birthdate;

    private String givenName;

    private String middleName;

    private String gender;

    private String phoneNumber;

    public static OidcUser of(DefaultOidcUser defaultOidcUser) {
        if (defaultOidcUser == null) {
            return null;
        }
        OidcUser oidcUser = new OidcUser();
        Collection<? extends GrantedAuthority> authorities = defaultOidcUser.getAuthorities();
        if (!CollectionUtils.isEmpty(authorities)) {
            List<OidcAuthority> authoritiesList = new ArrayList<>();
            for (GrantedAuthority authority : authorities) {
                if (authority instanceof OidcUserAuthority) {
                    authoritiesList.add(OidcAuthority.ofOidcUserAuthority((OidcUserAuthority) authority));
                } else if (authority instanceof SimpleGrantedAuthority) {
                    authoritiesList.add(OidcAuthority.ofSimpleGrantedAuthority((SimpleGrantedAuthority) authority));
                }
            }
            oidcUser.setAuthorities(authoritiesList);
        }
        oidcUser.setAttributes(JacksonUtils.toObject(defaultOidcUser.getClaims(), Claims.class));
        oidcUser.setIdToken(IdToken.of(defaultOidcUser.getIdToken()));
        oidcUser.setUserInfo(UserInfo.of(defaultOidcUser.getUserInfo()));
        oidcUser.setClaims(JacksonUtils.toObject(defaultOidcUser.getClaims(), Claims.class));
        oidcUser.setName(defaultOidcUser.getName());
        oidcUser.setIssuer(defaultOidcUser.getIssuer());
        oidcUser.setExpiresAt(defaultOidcUser.getExpiresAt());
        oidcUser.setIssuedAt(defaultOidcUser.getIssuedAt());
        oidcUser.setAuthorizationCodeHash(defaultOidcUser.getAuthorizationCodeHash());
        oidcUser.setAuthenticationMethods(defaultOidcUser.getAuthenticationMethods());
        oidcUser.setAuthenticationContextClass(defaultOidcUser.getAuthenticationContextClass());
        oidcUser.setNonce(defaultOidcUser.getNonce());
        oidcUser.setAudience(defaultOidcUser.getAudience());
        oidcUser.setAccessTokenHash(defaultOidcUser.getAccessTokenHash());
        oidcUser.setAuthorizedParty(defaultOidcUser.getAuthorizedParty());
        oidcUser.setAuthenticatedAt(defaultOidcUser.getAuthenticatedAt());
        oidcUser.setSubject(defaultOidcUser.getSubject());
        oidcUser.setAddress(Address.of(defaultOidcUser.getAddress()));
        oidcUser.setLocale(defaultOidcUser.getLocale());
        oidcUser.setFullName(defaultOidcUser.getFullName());
        oidcUser.setZoneInfo(defaultOidcUser.getZoneInfo());
        oidcUser.setProfile(defaultOidcUser.getProfile());
        oidcUser.setPreferredUsername(defaultOidcUser.getPreferredUsername());
        oidcUser.setPhoneNumberVerified(defaultOidcUser.getPhoneNumberVerified());
        oidcUser.setFamilyName(defaultOidcUser.getFamilyName());
        oidcUser.setNickName(defaultOidcUser.getNickName());
        oidcUser.setUpdatedAt(defaultOidcUser.getUpdatedAt());
        oidcUser.setPicture(defaultOidcUser.getPicture());
        oidcUser.setEmailVerified(defaultOidcUser.getEmailVerified());
        oidcUser.setWebsite(defaultOidcUser.getWebsite());
        oidcUser.setEmail(defaultOidcUser.getEmail());
        oidcUser.setBirthdate(defaultOidcUser.getBirthdate());
        oidcUser.setGivenName(defaultOidcUser.getGivenName());
        oidcUser.setMiddleName(defaultOidcUser.getMiddleName());
        oidcUser.setGender(defaultOidcUser.getGender());
        oidcUser.setPhoneNumber(defaultOidcUser.getPhoneNumber());
        return oidcUser;
    }

    public static DefaultOidcUser to(OidcUser oidcUser) {
        if (oidcUser == null) {
            return null;
        }
        List<OidcAuthority> authorities = oidcUser.getAuthorities();
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(authorities)) {
            for (OidcAuthority authority : authorities) {
                if (authority.getIdToken() != null) {
                    grantedAuthorities.add(OidcAuthority.toOidcUserAuthority(authority));
                } else {
                    grantedAuthorities.add(OidcAuthority.toSimpleGrantedAuthority(authority));
                }
            }
        }
        OidcIdToken oidcIdToken = IdToken.to(oidcUser.getIdToken());
        OidcUserInfo oidcUserInfo = UserInfo.to(oidcUser.getUserInfo());
        return new DefaultOidcUser(grantedAuthorities, oidcIdToken, oidcUserInfo);
    }

}
